<?php
/**
 * @description : the base captcha class.
 * @author      : stephen.mo <stephen.mo@hotmail.com>
 * @date        : Apr 1,2011
 */

class BasedCaptcha {

	public $text = '';
    
    /** Width of the image */
    public $width = 220;

    /** Height of the image */
    public $height = 80;

    /** Dictionary word file (empty for randnom text) */
    public $wordsFilePath = '';

    /** Min word length (for non-dictionary random text generation) */
    public $minWordLength = 5;

    /**
     * Max word length (for non-dictionary random text generation)
     * 
     * Used for dictionary words indicating the word-length
     * for font-size modification purposes
     */
    public $maxWordLength = 8;
	
    /** Sessionname to store the original text */
    public $session_var = 'captcha';

	/**
	 * nums of Background point 
	 */
	public $backgroundPoint = 200;

	/**
	 * is show Noise line 0:no show 1:show
	 */
	public $line = 0;

    /** Background color in RGB-array */
    public $backgroundColor = array(255, 255, 255);

    /** Foreground colors in RGB-array */
    public $colors = array(
        array(27, 78, 181),  // blue
        array(22, 163, 35),  // green
        array(214, 36, 7),   // red
    );

    /** Shadow color in RGB-array or false */
    public $shadowColor = false; //array(0, 0, 0);

    /**
     * Font configuration
     *
     * - font: TTF file
     * - spacing: relative pixel space between character
     * - minSize: min font size
     * - maxSize: max font size
     */
    public $fonts = array(
        'Duality' => array(
            'spacing' => - 2, 
            'minSize' => 30, 
            'maxSize' => 38, 
            'font' => 'Duality.ttf'), 
        'Heineken' => array(
            'spacing' => - 2, 
            'minSize' => 24, 
            'maxSize' => 34, 
            'font' => 'Heineken.ttf'));

    /** Wave configuracion in X and Y axes */
    public $Yperiod = 12;

    public $Yamplitude = 14;

    public $Xperiod = 11;

    public $Xamplitude =5;
   
    /** letter rotation clockwise */
    public $maxRotation = 8;

    /**
     * Internal image size factor (for better image quality)
     * 1: low, 2: medium, 3: high
     */
    public $scale = 2;

    /** 
     * Blur effect for better image quality (but slower image processing).
     * Better image results with scale=3
     */
    public $blur = false;

    /** Debug? */
    public $debug = false;

    /** Image format: jpeg or png */
    public $imageFormat = 'jpeg';

    /** GD image */
    public $im;

    public function __construct($config = array()) {
        if(empty($this->wordsFilePath)){
            $this->wordsFile = dirname(__FILE__) . '/Captcha/words/en.txt';
        }
        else{
            $this->wordsFile = $this->wordsFilePath;
        }
    }

    public function CreateImage() {
        $ini = microtime(true);
        
        /** Initialization */
        $this->ImageAllocate();

        /** Text insertion */
        $text = $this->GetCaptchaText();
		$this->text =  $text;
        $fontcfg = $this->fonts[array_rand($this->fonts)];
        $this->WriteText($text, $fontcfg);
        
        /** Transformations */
        $this->WaveImage();
        if($this->blur){
           imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR);
        }
		// Noise line
		$this->NoiseLine();

        $this->ReduceImage();
        if($this->debug){
            imagestring($this->im, 1, 1, $this->height - 8, "$text {$fontcfg['font']} " . round((microtime(true) - $ini) * 1000) . "ms", $this->GdFgColor);
        }
        /** Output */
        $this->WriteImage();
        $this->Cleanup();
    }

    public function OutImage($sText=NULL) {

        $ini = microtime(true);
        
        /** Initialization */
        $this->ImageAllocate();

        /** Text insertion */
        $text = $this->GetCaptchaText($sText);
		$this->text =  $text;
        $fontcfg = $this->fonts[array_rand($this->fonts)];
        $this->WriteText($text, $fontcfg);
        
        /** Transformations */
        $this->WaveImage();
        if($this->blur){
           imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR);
        }
		// Noise line
		$this->NoiseLine();

        $this->ReduceImage();
        if($this->debug){
            imagestring($this->im, 1, 1, $this->height - 8, "$text {$fontcfg['font']} " . round((microtime(true) - $ini) * 1000) . "ms", $this->GdFgColor);
        }
        /** Output */
        $out = $this->GetImage();
        $this->Cleanup();
        return $out;
    }    
	
	public function getText() {
		return $this->text;
    }

    /**
     * Add noise line the image
     */
	protected function NoiseLine() {
		if (!$this->line) return ;
	
		$w = rand(5,round($this->width/5));
        $h = round(($this->height * 27 / 40) * $this->scale-20);

		//Noise line
		for($i=0;$i<10;$i++){ 
			imageline($this->im, $w, $h+$i, $this->width* ($this->scale-0.1), $h+$i, $this->GdFgColor);
			//imageline($this->im, $w-10, $h+$i+20, $this->width* ($this->scale-0.1), $h+$i+20, $this->GdFgColor1);
		}
    }

    /**
     * Creates the image resources
     */
    protected function ImageAllocate() {
        // Cleanup
        if(! empty($this->im)){
            imagedestroy($this->im);
        }
		
        $this->im = imagecreatetruecolor($this->width * $this->scale, $this->height * $this->scale);
        $w = $this->width * $this->scale / 6;
        $h = $this->height * $this->scale / 2;

        // Background color
        $this->GdBgColor = imagecolorallocate($this->im, $this->backgroundColor[0], $this->backgroundColor[1], $this->backgroundColor[2]);
        imagefilledrectangle($this->im, 0, 0, $this->width * $this->scale, $this->height * $this->scale, $this->GdBgColor);

        // Foreground color
        $color = $this->colors[mt_rand(0, sizeof($this->colors) - 1)];
        $this->GdFgColor = imagecolorallocate($this->im, $color[0], $color[1], $color[2]);

		// Noise point
		for($i=0;$i<$this->backgroundPoint;$i++) {
			imagestring($this->im,5,rand(1,$this->width*2),rand(1,$this->height*2),"*",$this->GdFgColor);
		}

        // Shadow color
        if(! empty($this->shadowColor)){
            $this->GdShadowColor = imagecolorallocate($this->im, $this->shadowColor[0], $this->shadowColor[1], $this->shadowColor[2]);
        }
    }

    /**
     * Text generation
     *
     * @return string Text
     */
    protected function GetCaptchaText($text=NULL) {
        if (!empty($text)) return $text;
        $text = $this->GetDictionaryCaptchaText();
        if(! $text){
            $text = $this->GetRandomCaptchaText();
        }
        return $text;
    }
    

    /**
     * Random text generation
     *
     * @return string Text
     */
    protected function GetRandomCaptchaText($length = null)
    {
        if(empty($length)){
            $length = rand($this->minWordLength, $this->maxWordLength);
        }
        $words = "abcdefghijlmnopqrstvwyz";
        $vocals = "aeiou";
        $text = "";
        $vocal = rand(0, 1);
        for($i = 0; $i < $length; $i ++){
            if($vocal){
                $text .= substr($vocals, mt_rand(0, 4), 1);
            }else{
                $text .= substr($words, mt_rand(0, 22), 1);
            }
            $vocal = ! $vocal;
        }
        return $text;
    }

    /**
     * Random dictionary word generation
     *
     * @param boolean $extended Add extended "fake" words
     * @return string Word
     */
    function GetDictionaryCaptchaText($extended = false,$realtext=NULL) {
        if (!empty($realtext)) {
            return $realtext;
        }

        if(empty($this->wordsFile)){
            return false;
        }
        $fp = @fopen($this->wordsFile, "r");
        $length = strlen(@fgets($fp));
        if(! $length){
            return false;
        }
        $line = rand(0, (filesize($this->wordsFile) / $length) - 1);
        if(fseek($fp, $length * $line) == - 1){
            return false;
        }
        $text = trim(@fgets($fp));
        fclose($fp);
        /** Change ramdom volcals */
        if($extended){
            $text = str_split($text, 1);
            $vocals = array(
                'a', 
                'e', 
                'i', 
                'o', 
                'u');
            foreach($text as $i => $char){
                if(mt_rand(0, 1) && in_array($char, $vocals)){
                    $text[$i] = $vocals[mt_rand(0, 4)];
                }
            }
            $text = implode('', $text);
        }
        return $text;
    }

    /**
     * Text insertion
     */
    protected function WriteText($text, $fontcfg = array())
    {
        if(empty($fontcfg)){
            // Select the font configuration
            $fontcfg = $this->fonts[array_rand($this->fonts)];
        }
        $fontfile = dirname(__FILE__) . '/Captcha/fonts/' . $fontcfg['font'];

        /** Increase font-size for shortest words: 9% for each glyp missing */
        $lettersMissing = $this->maxWordLength - strlen($text);
        $fontSizefactor = 1 + ($lettersMissing * 0.09);

        // Text generation (char by char)
       // $x = 20 * $this->scale;
        $x = rand(40,round($this->width/3));
        $y = 15+round(($this->height * 27 / 40) * $this->scale);


        $length = strlen($text);
        for($i = 0; $i < $length; $i ++){
            $degree = rand($this->maxRotation * - 1, $this->maxRotation);
            $fontsize = rand($fontcfg['minSize'], $fontcfg['maxSize']) * $this->scale * $fontSizefactor;
            $letter = substr($text, $i, 1);
            if($this->shadowColor){
                $coords = imagettftext($this->im, $fontsize, $degree, $x + $this->scale, $y + $this->scale, $this->GdShadowColor, $fontfile, $letter);
            }
            $coords = imagettftext($this->im, $fontsize, $degree, $x, $y, $this->GdFgColor, $fontfile, $letter);
            $x += ($coords[2] - $x) + ($fontcfg['spacing'] * $this->scale);
        }
    }

    /**
     * Wave filter
     */
    protected function WaveImage()
    {

        // X-axis wave generation
        $xp = $this->scale * $this->Xperiod * rand(1, 3);
        $k = rand(0, 100);
        for($i = 0; $i < ($this->width * $this->scale); $i ++){
           imagecopy($this->im, $this->im, $i - 1, sin($k + $i / $xp) * ($this->scale * $this->Xamplitude), $i, 0, 1, $this->height * $this->scale);
        }
        // Y-axis wave generation
        $k = rand(0, 100);
        $yp = $this->scale * $this->Yperiod * rand(1, 2);
        for($i = 0; $i < ($this->height * $this->scale); $i ++){
            imagecopy($this->im, $this->im, sin($k + $i / $yp) * ($this->scale * $this->Yamplitude), $i - 1, 0, $i, $this->width * $this->scale, 1);
           
        }
    }

    /**
     * Reduce the image to the final size
     */
    protected function ReduceImage()
    {
        // Reduzco el tamao de la imagen
        $imResampled = imagecreatetruecolor($this->width, $this->height);
        imagecopyresampled($imResampled, $this->im, 0, 0, 0, 0, $this->width, $this->height, $this->width * $this->scale, $this->height * $this->scale);
        imagedestroy($this->im);
        $this->im = $imResampled;
    }

    /**
     * File generation
     */
    protected function WriteImage()
    {
    	if($this->imageFormat == 'png'){
            //header("Content-type: image/png");
            return imagepng($this->im);
        }else{
            //header("Content-type: image/jpeg");
           return imagejpeg($this->im, null, 80);
        }
        
    }

    /**
     * File generation
     */
    protected function GetImage()
    {
    	ob_start();
        //header("Content-type: image/jpeg");
        imagejpeg($this->im, null, 80);
        $out = ob_get_contents();
        ob_end_clean();
        return $out;
        
    }
        
	/**
     * Cleanup
     */
    protected function Cleanup()
    {
        imagedestroy($this->im);
    }
}
?>
